﻿using System;
using System.Collections.Generic;
using System.Windows.Forms;

using WFL.Windows;

namespace WFL.Windows.Forms
{
    public class HotKey : IDisposable
    {
        /// <summary>
        /// Win修改键
        /// </summary>
        public const Keys Win = (Keys)0x80000;

        public static bool RegisterHotKey(IntPtr handle, int id, Keys keys)
        {
            uint modifiers = 0;
            if ((keys & Keys.Alt) == Keys.Alt)
            {
                modifiers |= Header.Winuser.MOD_ALT;
            }
            if ((keys & Keys.Control) == Keys.Control)
            {
                modifiers |= Header.Winuser.MOD_CONTROL;
            }
            if ((keys & Keys.Shift) == Keys.Shift)
            {
                modifiers |= Header.Winuser.MOD_SHIFT;
            }
            if ((keys & HotKey.Win) == HotKey.Win)
            {
                modifiers |= Header.Winuser.MOD_WIN;
            }
            uint keyCode = (uint)(keys & Keys.KeyCode);
            int result = API.User32.RegisterHotKey(handle, id, modifiers, keyCode);
            return (result != 0);
        }

        public static bool UnregisterHotKey(IntPtr handle, int id)
        {
            int result = API.User32.UnregisterHotKey(handle, id);
            return (result != 0);
        }

        public static Keys GetHotKey(IntPtr lParam)
        {
            int modifiers = (lParam.ToInt32() & 0xffff);
            int keyCode = ((lParam.ToInt32() >> 16) & 0xffff);
            Keys keys = Keys.None;
            if ((modifiers & Header.Winuser.MOD_ALT) == Header.Winuser.MOD_ALT)
            {
                keys |= Keys.Alt;
            }
            if ((modifiers & Header.Winuser.MOD_CONTROL) == Header.Winuser.MOD_CONTROL)
            {
                keys |= Keys.Control;
            }
            if ((modifiers & Header.Winuser.MOD_SHIFT) == Header.Winuser.MOD_SHIFT)
            {
                keys |= Keys.Shift;
            }
            if ((modifiers & Header.Winuser.MOD_WIN) == Header.Winuser.MOD_WIN)
            {
                //Win修改键（类似于Control修改键），找不到对应的枚举值（只有Keys.LWin键和Keys.RWin键，没有Keys.Win修改键）
                keys |= HotKey.Win;
            }
            keys |= (Keys)keyCode;
            return keys;
        }



        private SubclassNativeWindow _wnd;
        private bool _disposed;

        public event HotKeyPressedEventHandler HotKeyPressed;

        public IntPtr Handle
        {
            get { return _wnd.Handle; }
        }

        public HotKey(IntPtr handle)
        {
            if (handle == IntPtr.Zero)
            {
                throw new ArgumentException("无效的窗口句柄。");
            }
            _wnd = new SubclassNativeWindow();
            _wnd.HotKeyPressed += new HotKeyPressedEventHandler(Wnd_HotKeyPressed);
            _wnd.AssignHandle(handle);
        }

        private void OnHotKeyPressed(HotKeyPressedEventArgs e)
        {
            if (HotKeyPressed != null)
            {
                HotKeyPressed(this, e);
            }
        }

        public bool Register(int id, Keys keys)
        {
            ValidateObjectState();

            IntPtr handle = Handle;
            bool result = RegisterHotKey(handle, id, keys);
            if (result)
            {
                if (!_wnd.IdList.Contains(id))
                {
                    _wnd.IdList.Add(id);
                }
            }
            return result;
        }

        public bool Unregister(int id)
        {
            ValidateObjectState();

            IntPtr handle = Handle;
            bool result = UnregisterHotKey(handle, id);
            if (result)
            {
                _wnd.IdList.Remove(id);
            }
            return result;
        }

        private void Wnd_HotKeyPressed(object sender, HotKeyPressedEventArgs e)
        {
            OnHotKeyPressed(e);
        }

        private void ValidateObjectState()
        {
            if (_disposed)
            {
                throw new ObjectDisposedException(typeof(HotKey).Name);
            }
        }


        #region IDisposable 成员

        public void Dispose()
        {
            if (!_disposed)
            {
                _disposed = true;
                foreach (int id in _wnd.IdList)
                {
                    UnregisterHotKey(Handle, id);
                }
                _wnd.IdList.Clear();
                _wnd.ReleaseHandle();
                _wnd.HotKeyPressed -= new HotKeyPressedEventHandler(Wnd_HotKeyPressed);
                _wnd = null;
            }
        }

        #endregion


        #region SubclassNativeWindow

        private class SubclassNativeWindow : NativeWindow
        {
            public event HotKeyPressedEventHandler HotKeyPressed;

            private IList<int> _idList;
            public IList<int> IdList
            {
                get { return _idList; }
            }

            public SubclassNativeWindow()
            {
                _idList = new List<int>();
            }

            private void OnHotKeyPressed(HotKeyPressedEventArgs e)
            {
                if (HotKeyPressed != null)
                {
                    HotKeyPressed(this, e);
                }
            }

            protected override void WndProc(ref Message m)
            {
                switch (m.Msg)
                {
                    default:
                        base.WndProc(ref m);
                        break;
                    case Header.Winuser.WM_HOTKEY:
                        int id = m.WParam.ToInt32();
                        Keys keys = GetHotKey(m.LParam);
                        HotKeyPressedEventArgs e = new HotKeyPressedEventArgs(Handle, id, keys);
                        OnHotKeyPressed(e);
                        break;
                }
            }
        }

        #endregion


        #region MessageFilter

        public class MessageFilter : IMessageFilter
        {
            public event HotKeyPressedEventHandler HotKeyPressed;

            public MessageFilter()
            {

            }

            private void OnHotKeyPressed(HotKeyPressedEventArgs e)
            {
                if (HotKeyPressed != null)
                {
                    HotKeyPressed(this, e);
                }
            }


            #region IMessageFilter 成员

            public bool PreFilterMessage(ref Message m)
            {
                switch (m.Msg)
                {
                    default:
                        return false;
                    case Header.Winuser.WM_HOTKEY:
                        if (m.HWnd != IntPtr.Zero)
                        {
                            return false;
                        }
                        int id = m.WParam.ToInt32();
                        Keys keys = GetHotKey(m.LParam);
                        HotKeyPressedEventArgs e = new HotKeyPressedEventArgs(IntPtr.Zero, id, keys);
                        OnHotKeyPressed(e);
                        return e.Suppress;
                }
            }

            #endregion
        }

        #endregion



    }
}
